﻿using System;
using System.Runtime.Serialization;
using System.Threading;
using MugenMvvmToolkit.Utils;

namespace MugenMvvmToolkit.Infrastructure
{
    /// <summary>
    ///     Represents the class to detect dead locks.
    /// </summary>
    public class DeadLockDetector : IDisposable
    {
        #region Fields

        internal const string ErrorMessage =
            @"Timeout waiting for lock. 
This may mean that the application has deadlock.
You can also increase the value of waiting for the lock (GlobalSettings.DeadLockWaitTime) or disable the detection of deadlocks (GlobalSettings.DeadLockDetectionEnabled = false).";
        private readonly object _locker;

        #endregion

        #region Constructors

        private DeadLockDetector(object o)
        {
            _locker = o;
        }

        #endregion

        #region Methods

        /// <summary>
        ///     Locks the specified object.
        /// </summary>
        internal static DeadLockDetector Lock(object o)
        {
            return Lock(o, GlobalSettings.GetLockTimeout(), ErrorMessage);
        }

        /// <summary>
        ///     Locks the specified object.
        /// </summary>
        internal static DeadLockDetector Lock(object o, string message)
        {
            return Lock(o, GlobalSettings.GetLockTimeout(), string.Format("{0}\n{1}", ErrorMessage, message));
        }

        /// <summary>
        ///     Locks the specified object.
        /// </summary>
        public static DeadLockDetector Lock(object o, TimeSpan timeout, string message)
        {
            var tl = new DeadLockDetector(o);
            if (!Monitor.TryEnter(o, timeout))
                Thrower.LockTimeout(message);
            return tl;
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        ///     Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <filterpriority>2</filterpriority>
        public void Dispose()
        {
            Monitor.Exit(_locker);
        }

        #endregion
    }

    /// <summary>
    ///     Represents the exception class to detect dead locks.
    /// </summary>
#if WPF
    [Serializable]
#endif
    public class LockTimeoutException : Exception
    {
        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="T:System.Exception" /> class with a specified error message.
        /// </summary>
        /// <param name="message">The message that describes the error. </param>
        public LockTimeoutException(string message)
            : base(message)
        {
        }

#if WPF
        /// <summary>
        ///     Initializes a new instance of the <see cref="T:System.Exception" /> class with serialized data.
        /// </summary>
        /// <param name="info">
        ///     The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.
        /// </param>
        /// <param name="context">
        ///     The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.
        /// </param>
        /// <exception cref="T:System.ArgumentNullException">
        ///     The <paramref name="info" /> parameter is null.
        /// </exception>
        /// <exception cref="T:System.Runtime.Serialization.SerializationException">
        ///     The class name is null or <see cref="P:System.Exception.HResult" /> is zero (0).
        /// </exception>
        protected LockTimeoutException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
#endif

        #endregion
    }
}